﻿using EventBus.Abstractions;
using EventBus.Events;
using System;
using System.Collections.Generic;
using System.Linq;

namespace EventBus
{
    /// <summary>
    /// 基于内存的事件订阅管理器
    /// </summary>
    public class InMemoryEventBusSubscriptionsManager : IEventBusSubscriptionsManager
    {
        #region properties&fields

        /// <summary>
        /// 订阅（消费者）集合
        /// </summary>
        private readonly Dictionary<string, List<SubscriptionInfo>> _handlers;

        /// <summary>
        /// 事件类型列表
        /// </summary>
        private readonly List<Type> _eventTypes;

        /// <summary>
        /// 标识是否有需要处理的事件
        /// </summary>
        public bool IsEmpty => !_handlers.Any();

        /// <summary>
        /// 事件移除方法委托
        /// </summary>
        public event EventHandler<string> OnEventRemoved;

        #endregion

        #region ctors

        public InMemoryEventBusSubscriptionsManager()
        {
            _handlers = new Dictionary<string, List<SubscriptionInfo>>();
            _eventTypes = new List<Type>();
        }

        #endregion

        #region methods

        /// <summary>
        /// 添加动态类型订阅
        /// </summary>
        /// <typeparam name="TH">事件处理器</typeparam>
        /// <param name="eventName">事件key</param>
        public void AddDynamicSubscription<TH>(string eventName) 
            where TH : IDynamicIntegrationEventHandler
        {
            DoAddSubscription(typeof(TH), eventName, true);
        }

        /// <summary>
        /// 添加事件订阅
        /// </summary>
        /// <typeparam name="T">事件</typeparam>
        /// <typeparam name="TH">事件处理器</typeparam>
        public void AddSubscription<T, TH>()
            where T : IntegrationEvent
            where TH : IIntegrationEventHandler<T>
        {
            var eventKey = GetEventName<T>();

            DoAddSubscription(typeof(TH), eventKey, isDynamic: false);
            if(!_eventTypes.Contains(typeof(T)))
            {
                _eventTypes.Add(typeof(T));
            }
        }

        private void DoAddSubscription(Type handlerType, string eventName, bool isDynamic)
        {
            if(!HasSubscriptionForEvent(eventName))
            {
                _handlers.Add(eventName, new List<SubscriptionInfo>());
            }

            if(_handlers[eventName].Any(s=>s.HandlerType==handlerType))
            {
                throw new ArgumentException(
                    $"Handler Type {handlerType.Name} already registered for '{eventName}'", nameof(handlerType));
            }

            if(isDynamic)
            {
                _handlers[eventName].Add(SubscriptionInfo.Dynamic(handlerType));
            }
            else
            {
                _handlers[eventName].Add(SubscriptionInfo.Typed(handlerType));
            }
        }

        /// <summary>
        /// 清除事件处理器（消费者）
        /// </summary>
        public void Clear() => _handlers.Clear();

        /// <summary>
        /// 获取事件名称
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <returns></returns>
        public string GetEventName<T>()
        {
            return typeof(T).Name;
        }

        /// <summary>
        /// 根据事件名称获取事件类型
        /// </summary>
        /// <param name="eventName"></param>
        /// <returns></returns>
        public Type GetEventTypeByName(string eventName) => _eventTypes.SingleOrDefault(e => e.Name == eventName);

        /// <summary>
        /// 根据事件名称获取事件处理器（消费者）
        /// </summary>
        /// <param name="eventName"></param>
        /// <returns></returns>
        public IEnumerable<SubscriptionInfo> GetHandlerForEvent(string eventName) => _handlers[eventName];

        /// <summary>
        /// 获取指定事件的处理器（消费者）
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <returns></returns>
        public IEnumerable<SubscriptionInfo> GetHandlersForEvent<T>() 
            where T : IntegrationEvent
        {
            var eventName = GetEventName<T>();
            return GetHandlerForEvent(eventName);
        }

        /// <summary>
        /// 根据事件类型标识是否有订阅指定事件
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <returns></returns>
        public bool HasSubscriptionForEvent<T>() 
            where T : IntegrationEvent
        {
            var eventName = GetEventName<T>();
            return HasSubscriptionForEvent(eventName);
        }

        /// <summary>
        /// 根据事件名称标识是否有订阅指定事件
        /// </summary>
        /// <param name="eventName">事件名称</param>
        /// <returns></returns>
        public bool HasSubscriptionForEvent(string eventName) => _handlers.ContainsKey(eventName);

        /// <summary>
        /// 移除动态事件订阅
        /// </summary>
        /// <param name="eventName">事件名称</param>
        /// <typeparam name="TH"></typeparam>
        public void RemoveDynamicSubscription<TH>(string eventName) 
            where TH : IDynamicIntegrationEventHandler
        {
            var handlerToRemove = FindDynamicSubscriptionToRemove<TH>(eventName);
            DoRemoveHandler(eventName, handlerToRemove);
        }

        /// <summary>
        /// 移除事件订阅
        /// </summary>
        /// <typeparam name="T">事件</typeparam>
        /// <typeparam name="TH">事件处理器</typeparam>
        public void RemoveSubscription<T, TH>()
            where T : IntegrationEvent
            where TH : IIntegrationEventHandler<T>
        {
            var handlerToRemove = FindSubscriptionToRemove<T, TH>();
            var eventName = GetEventName<T>();
            DoRemoveHandler(eventName, handlerToRemove);
        }

        /// <summary>
        /// 根据事件名称查找订阅信息
        /// </summary>
        /// <typeparam name="TH">事件处理器</typeparam>
        /// <param name="eventName">事件名称</param>
        /// <returns></returns>
        private SubscriptionInfo FindDynamicSubscriptionToRemove<TH>(string eventName)
            where TH : IDynamicIntegrationEventHandler
        {
            return DoFindSubscriptionToRemove(eventName, typeof(TH));
        }

        /// <summary>
        /// 根据类型查找订阅信息
        /// </summary>
        /// <typeparam name="T">事件</typeparam>
        /// <typeparam name="TH">事件处理器</typeparam>
        /// <returns></returns>
        private SubscriptionInfo FindSubscriptionToRemove<T,TH>()
             where T : IntegrationEvent
             where TH : IIntegrationEventHandler<T>
        {
            var eventName = GetEventName<T>();
            return DoFindSubscriptionToRemove(eventName, typeof(TH));
        }

        /// <summary>
        /// 根据事件名称查找订阅信息
        /// </summary>
        /// <param name="eventName">事件名称</param>
        /// <param name="handlerType">事件处理器类型</param>
        /// <returns></returns>
        private SubscriptionInfo DoFindSubscriptionToRemove(string eventName,Type handlerType)
        {
            if(!HasSubscriptionForEvent(eventName))
            {
                return null;
            }
            return _handlers[eventName].FirstOrDefault(s => s.HandlerType == handlerType);
        }

        /// <summary>
        /// 移除事件订阅（消费者）
        /// </summary>
        /// <param name="eventName">事件名称</param>
        /// <param name="subsToRemove">需要移除的订阅信息</param>
        private void DoRemoveHandler(string eventName,SubscriptionInfo subsToRemove)
        {
            if(subsToRemove!=null)
            {
                _handlers[eventName].Remove(subsToRemove);
                if(!_handlers.Any())
                {
                    _handlers.Remove(eventName);
                    var eventType = _eventTypes.FirstOrDefault(e => e.Name == eventName);
                    if(eventType!=null)
                    {
                        _eventTypes.Remove(eventType);
                    }
                    RasieOnEventRemoved(eventName);
                }
            }
        }

        /// <summary>
        /// 调用移除方法
        /// </summary>
        /// <param name="eventName"></param>
        private void RasieOnEventRemoved(string eventName)
        {
            var handler = OnEventRemoved;
            handler?.Invoke(this, eventName);
        }

        #endregion
    }
}
